高德地图实现3D建筑多楼层模型相关代码

您所在的位置:网站首页 高德地图 3d地图 高德地图实现3D建筑多楼层模型相关代码

高德地图实现3D建筑多楼层模型相关代码

2023-10-01 18:49| 来源: 网络整理| 查看: 265

前言:

因在哔哩哔哩发布过相关视频,收到小伙伴的留言。所以在此讲解,希望能帮到大家

首先附上效果图:

 

开发文档:绘制步骤-3D立体图形-教程-地图 JS API | 高德地图API (amap.com)

基础 Mesh-立体 Mesh-示例中心-JS API 示例 | 高德地图API (amap.com)

开发环境:

后端接口是java,前端是vue、js 在这里只做前端代码讲解,后端只是基本数据

正式开始:

(讲解不周到的地方希望读者指出)

可能小伙伴们困扰的是如何实现多楼层

由官方给的示例,即上述第二个链接,看到的效果图入下:

只需将此处做修改,即可有悬空效果:

 若要实现多楼层,只需将同样的经纬度集合,循环遍历,取不同的高度值即可实现

 在开发过程中,还有一些类似点击、描边的需求,同时还有诸如旋转后,几何体看起来不封闭以及建筑物的地标文字不在模型中央上方的问题

以下附上关键代码,文末附上所有代码

点击事件:(其中有些函数可以在开发文档中找到)

// prism 拾取 map.on('mousedown', function (ev) { var pixel = ev.pixel; var px = new AMap.Pixel(pixel.x, pixel.y); var obj = map.getObject3DByContainerPos(px, [object3Dlayer], false) || {}; // 选中的 object3D 对象,这里为当前 Mesh var object = obj.object; // 被拾取到的对象和拾取射线的交叉点的3D坐标 clickMesh(object, _this); }); /** * 点击模型事件处理 * @param obj * @param _this */ function clickMesh(obj, _this) { let pool3d = pool3ds.find(function (one) { return one.mesh === obj; }); pool3ds.forEach(function (pool) { if (pool.change === 1) { pool3ds.slice(pool, 1); updateMeshColor(pool, pool.color, 'reset'); pool3ds.push(pool); } }); if (pool3d) { pool3ds.slice(pool3d, 1); updateMeshColor(pool3d, selectColor, 'change'); pool3ds.push(pool3d); _this.globalVariable.buildingId = pool3d.building.buildingId; _this.showUnitTab(pool3d.fc); } }

横线或竖线:

function drawVerticalBar(lngLat, endH) { let Line3D = new AMap.Object3D.Line(); let Lgeometry = Line3D.geometry; let origin = map.lngLatToGeodeticCoord(lngLat); Lgeometry.vertices.push(origin.x, origin.y, -endH); Lgeometry.vertexColors.push(9 / 255, 0 / 255, 0 / 255, 0.99); let des = map.lngLatToGeodeticCoord(lngLat); Lgeometry.vertices.push(des.x, des.y, 0); Lgeometry.vertexColors.push(9 / 255, 0 / 255, 0 / 255, 0.99); object3Dlayer.add(Line3D); } function drawLine(bounds, height, color, lineW) { if (bounds == null || bounds.length === 0) { return; } let lineBounds = []; let lineHeight = []; for (let i = 0; i < bounds.length; i++) { lineHeight.push(height); lineBounds.push(bounds[i]); } lineHeight.push(height); lineBounds.push(bounds[0]); let line = new AMap.Object3D.MeshLine({ path: lineBounds, height: lineHeight, color: color, width: lineW }); object3Dlayer.add(line); }

几何体封闭:

... mesh.backOrFront = 'both'; // 关键位置 mesh.transparent = transparent; object3Dlayer.add(mesh); return mesh; ...

地标文字在模型中央上方:(原理:取集合面积的重心)

public class LatLngVO { private Double lat; private Double lng; } /** * @param vo 重心 * @param voList 多边形经纬度集合 */ private static void calculateCenter(LatLngVO vo, List voList) { double pointX = 0.0; double pointY = 0.0; double totalArea = 0.0; LatLngVO p1 = voList.get(1); for (int i = 2; i < voList.size(); i++) { LatLngVO p2 = voList.get(i); double area = calculateArea(voList.get(0), p1, p2); totalArea += area; pointX += (voList.get(0).getLng() + p1.getLng() + p2.getLng()) * area; pointY += (voList.get(0).getLat() + p1.getLat() + p2.getLat()) * area; p1 = p2; } if (totalArea != 0.0) { vo.setLng(pointX / totalArea / 3); vo.setLat(pointY / totalArea / 3); } } private static double calculateArea(LatLngVO p0, LatLngVO p1, LatLngVO p2) { double area = p0.getLng() * p1.getLat() + p1.getLng() * p2.getLat() + p2.getLng() * p0.getLat() - p1.getLng() * p0.getLat() - p2.getLng() * p1.getLat() - p0.getLng() * p2.getLat(); return area / 2 ; }

在附上所有代码之前。我对当时所实现的产品功能和业务场景做大致讲解:

业务场景:

1、有project、buinding、unit三个核心实体,

即项目 -> 物业 -> 单元,均为一对多关系

2、在开发3d之前。已有2维描边基础,所以有现成经纬度

产品功能:

1、按照录入的楼层层高、数量、是否在租等信息展示模型

2、点击模型时,改变颜色或者显示相关弹窗等

重点:

1、构建模型时可能存在楼层悬空后,楼层之间区分不明显,此时可以在中间塞入一个高度值较小、且不同颜色的模型模拟地板的效果

2、模型的点击事件不是很准确(这是必然的,和角度也有一点关系)

代码主要是前端js、vue代码,仅供参考,若有不明确之处,还望多多实验,或评论留言,

附上的代码中 我去掉了一些无关代码,若用心阅读,应该是能理解大致含义的,主要关注方法initAMap即可

import Vue from 'vue'; import AMap from 'AMap'; import AMapUI from 'AMapUI'; import mapUtil from '../../../utils/mapUtils'; import emptyUtil from '../../../utils/emptyUtils' import $ from 'jquery'; Vue.prototype.$message = Message; let mapType = { weixing : '#map-3d > div.amap-ui-control-container.amap-ui-retina.amap-ui-control-position-rt.amap-ui-control-theme-light > div > form > div.amap-ui-control-layer-base > div.amap-ui-control-layer-base-item.amap-ui-control-layer-base-item-satellite > label', standard : '#map-3d > div.amap-ui-control-container.amap-ui-retina.amap-ui-control-position-rt.amap-ui-control-theme-light > div > form > div.amap-ui-control-layer-base > div.amap-ui-control-layer-base-item.amap-ui-control-layer-base-item-tile > label' }; let map; let object3Dlayer; let pool3ds = []; let texts = []; let sdh = 31; let selectColor = [255 / 255, 245 / 255, 47 / 255, 0.9]; let noUnAreaColor = [131 / 255, 131 / 255, 131 / 255, 0.61]; let hasUnAreaColor = [10 / 255, 183 / 255, 168 / 255, 0.66]; let floorLineC = [9 / 255, 0 / 255, 0 / 255, 0.99]; let landLineC = [6 / 255, 221 / 255, 255 / 255, 1]; // let fc = "rgb(6,221,255)"; let landFaceC = [236 / 255, 245 / 255, 255 / 255, 0.35]; export default { data() { return { mapType : 0, h3d: 30, position: { lng: null, lat: null, }, enums: { }, project: { }, globalVariable: { buildingId: null, unitId: null, building: null, }, buildingList: [], unit: { unitTab: [], unitActive: '', floorActive: '', }, editUnit: { }, map3d: { projectId: null, buildings: [], lat: null, lng: null, landPoints: [], moveTable: [], }, visible: { }, editName: { id: null, name: '', }, center: { lng: null, lat: null, }, loading: {}, rules: {}, copy: { unitList: [], floorNumbers: [], unitId: null, buildingId: null, buildingList: [], }, } }, components: { PriceSetting: PriceSetting, UnitDetail: UnitDetail, }, mounted() { this.initAMap(); }, methods: { echo3dModel() { this.initAMap(); this.visible.removeModelVisible = false; }, remove3dModel() { map.remove(object3Dlayer); pool3ds = []; texts.forEach(t => { t.setMap(null); }); texts = []; this.visible.removeModelVisible = true; }, addOneUnit() { this.resetUnit(); this.visible.addUnitVisible = true; dealWithAddUnit(this); }, cutoverMapType() { if (this.mapType === 1) { $(mapType.standard).click(); this.mapType = 0; return } if (this.mapType === 0) { $(mapType.weixing).click(); this.mapType = 1; } }, copyUnitVue() { if (this.copy.floorNumbers.length === 0) { this.$message.warning('请选择目标楼层'); return; } let buildingId = this.copy.buildingId; let unitId = this.copy.unitId; let floorNumber = this.copy.floorNumbers.join(); this.$axios.get(this.$pmsPath + '/cms/rentalUnit/batchCopy.do?originUnitId=' + unitId + '&targetBuildingId=' + buildingId + '&floorNumbers=' + floorNumber) .then(res => { if (res.code === '0') { this.visible.copyRentalUnitDialog = false; this.$message.success('复制成功'); pool3ds = []; this.initAMap(); let _this = this; setTimeout(function () { _this.showUnitTab(floorNumber); }, 1000); } else { this.$message.error('复制失败 :' + res.msg); } }); }, changeH3d() { this.initAMap(); }, resetPosition() { map.setCenter([this.position.lng, this.position.lat]); }, unitCutover(tab) { this.globalVariable.unitId = tab.label.replace('单元#', ''); this.resetUnit(); this.unitInfo(); }, initAMap() { this.map3d.projectId = this.$route.params.projectId; this.queryAllBuilding(); this.sidebarInfo.projectId = this.map3d.projectId; this.$axios.get(this.$pmsPath + '/cms/project/gd3dMap.do?projectId=' + this.map3d.projectId) .then(res => { if (res.code === '0') { let data = res.data; this.map3d.lat = data.lat; this.map3d.lng = data.lng; this.map3d.landPoints = data.landPoints; this.map3d.buildings = data.buildings; this.sidebarInfo.parkName = data.parkName; this.sidebarInfo.buildingIds = data.buildingIds; this.sidebarInfo.addressDesc = data.addressDesc; sdh = this.h3d; draw(this); } else { this.map3d.lat = 31.220946; this.map3d.lng = 121.4181333; } }); }, queryAllBuilding() { this.$axios.get(this.$pmsPath + '/cms/project/buildings3d.do?projectId=' + this.map3d.projectId) .then(res => { if (res.code === '0') { this.buildingList = res.data; } }); }, drawBuilding(id, pathList, building) { this.$axios.get(this.$pmsPath + '/cms/project/centerPoint.do?id=' + id) .then(res => { if (res.code === '0') { let center = res.data; this.center.lng = center.lng; this.center.lat = center.lat; initMesh(pathList, building, this); } }); }, } } function dealWithAddUnit(_this) { let building = null; _this.buildingList.forEach(b => { if (b.id === _this.globalVariable.buildingId) { building = b; } }); _this.resetUnit(); _this.editUnit.buildingId = _this.globalVariable.buildingId; _this.globalVariable.unitId = null; _this.editUnit.warehouseType = building.warehouseType; } function draw(_this) { let curr = mapUtil.bd_google_encrypt(_this.map3d.lat, _this.map3d.lng); map = new AMap.Map('map-3d', { viewMode: '3D', // 开启 3D 模式 pitch: 52, rotation: 60, center: [curr.lon, curr.lat], features: ['bg', 'road'], zoom: 17, buildingAnimation:true, mapStyle: "amap://styles/fresh" }); _this.position.lng = curr.lon; _this.position.lat = curr.lat; addPlugin(); map.AmbientLight = new AMap.Lights.AmbientLight([1, 1, 1], 0.5); map.DirectionLight = new AMap.Lights.DirectionLight([1, -1, 2], [1, 1, 1], 0.8); object3Dlayer = new AMap.Object3DLayer(); map.add(object3Dlayer); for (let bx = 0; bx < _this.map3d.buildings.length; bx++) { let building = _this.map3d.buildings[bx]; let coordinates = building.coordinates; // 没有描边 没有层高 没有楼层数 不显示立体 if (coordinates === null || coordinates === '' || building.firstHeight == null || building.floorCount


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3